#pragma once

#include <atomic>
#include <cassert>
#include <cstdarg>
#include <memory>
#include <mutex>
#include <iostream>

#include "threadpool.hpp"
#include "level.hpp"
#include "format.hpp"
#include "asyncworker.hpp"
#include "asyncbuffer.hpp"
#include "logflush.hpp"
#include "logbackup.hpp"

namespace Log
{
    class AsyncWorker;
    using LogFlushPtr = std::shared_ptr<LogFlushInterface>;
    using AsyncWorkerPtr = std::shared_ptr<AsyncWorker>;

    class AsyncLogger
    { 
    public:
        const std::string Name() const { return name_; }
        AsyncLogger(const std::string& name, std::vector<LogFlushPtr>& flushes, AsyncType type)
            : name_(name)
            , flushs_(flushes)
            , asyncWorker_(std::make_shared<AsyncWorker>(std::bind(&AsyncLogger::KeepFlush, this, std::placeholders::_1), type))
        {
        }

        void Debug(const std::string &file, size_t line, const std::string format, ...)
        {
            // 获取可变参数列表中的格式
            va_list va;
            va_start(va, format);
            char *ret;
            int r = vasprintf(&ret, format.c_str(), va);
            if (r == -1)
                perror("vasprintf failed!!!: ");
            va_end(va); // 将va指针置空

            serialize(LogLevel::value::DEBUG, file, line,
                    ret); // 生成格式化日志信息并写文件

            free(ret);
            ret = nullptr;
        };
        void Info(const std::string &file, size_t line, const std::string format,
                ...)
        {
            va_list va;
            va_start(va, format);
            char *ret;
            int r = vasprintf(&ret, format.c_str(), va);
            if (r == -1)
                perror("vasprintf failed!!!: ");
            va_end(va);

            serialize(LogLevel::value::INFO, file, line,
                    ret);

            free(ret);
            ret = nullptr;
        };

        void Warn(const std::string &file, size_t line, const std::string format,
                ...)
        {
            va_list va;
            va_start(va, format);
            char *ret;
            int r = vasprintf(&ret, format.c_str(), va);
            if (r == -1)
                perror("vasprintf failed!!!: ");
            va_end(va);

            serialize(LogLevel::value::WARN, file, line,
                    ret);
            free(ret);
            ret = nullptr;
        };
        void Error(const std::string &file, size_t line, const std::string format,
                    ...)
        {
            va_list va;
            va_start(va, format);
            char *ret;
            int r = vasprintf(&ret, format.c_str(), va);
            if (r == -1)
                perror("vasprintf failed!!!: ");
            va_end(va);

            serialize(LogLevel::value::ERROR, file, line,
                    ret);

            free(ret);
            ret = nullptr;
        };
        void Fatal(const std::string &file, size_t line, const std::string format,
                    ...)
        {
            va_list va;
            va_start(va, format);
            char *ret;
            int r = vasprintf(&ret, format.c_str(), va);
            if (r == -1)
                perror("vasprintf failed!!!: ");
            va_end(va);

            serialize(LogLevel::value::FATAL, file, line,
                    ret);

            free(ret);
            ret = nullptr;
        };

    protected:

        void serialize(LogLevel::value level, const std::string &file, size_t line, char *ret)
        {
            LogFormat msg(level, file, line, name_, ret);
            std::string data = msg.format();

            if (level == LogLevel::value::FATAL || level == LogLevel::value::ERROR)
            {
                try
                {
                    auto ret = ThreadPool::GetInstance().enqueue(start_backup, data);
                    ret.get();
                }
                catch (const std::runtime_error &e)
                {
                    std::cout << __FILE__ << __LINE__ << "thread pool closed" << std::endl;
                }
            }

            //获取到string类型的日志信息后就可以输出到异步缓冲区了，异步工作器后续会对其进行刷盘
            Push(data.c_str(), data.size());
        }

        void KeepFlush(Buffer &buffer)
        {
            if (flushs_.empty())
                return;
            for (auto &flush : flushs_)
            {  
                flush->Flush(buffer.Begin(), buffer.ReadableSize());
            }
        }
        
        void Push(const char *data, size_t len)
        {
            // Push函数本身是线程安全的，这里不加锁
            asyncWorker_->Push(data, len); 
        }

    private:
        std::mutex mtx_;
        std::string name_;
        std::vector<LogFlushPtr> flushs_;
        AsyncWorkerPtr asyncWorker_;
    };




    using AsyncLoggerPtr = std::shared_ptr<AsyncLogger>;
    // 日志器建造
    class LoggerBuilder
    {
    public:
        using ptr = std::shared_ptr<LoggerBuilder>;
        void BuildLoggerName(const std::string &name) 
        { 
            logger_name_ = name; 
        }
        void BuildLopperType(AsyncType type) 
        { 
            async_type_ = type; 
        }


        template <typename FlushType, typename... Args>
        void BuildLoggerFlush(Args &&...args)
        {
            flushs_.emplace_back(LogFlushFactory::CreateLog<FlushType>(std::forward<Args>(args)...));
        }

        AsyncLoggerPtr Build()
        {
            assert(logger_name_.empty() == false);
            
            // 如果写日志方式没有指定，那么采用默认的标准输出
            if (flushs_.empty())
            {
                flushs_.emplace_back(std::make_shared<Log::StdoutFlushImpl>());
            }

            return std::make_shared<AsyncLogger>(logger_name_, flushs_, async_type_);
        }

    protected:
        std::string logger_name_ = "async_logger"; // 日志器名称
        std::vector<LogFlushPtr> flushs_; // 写日志方式
        AsyncType async_type_ = AsyncType::ASYNC_SAFE;//用于控制缓冲区是否增长
    };
}